package client;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import Messages.Message;
import Messages.MessageType;
import Messages.RequestLockMessage;
import Messages.ResponseLockMessage;
import Utility.ClientUtilities;
import Utility.Constants;
import Utility.Host;
import Utility.PaxosMessageReader;
import Utility.PaxosMessageReaderImpl;
import Utility.PaxosMessageWriter;
import Utility.PaxosMessageWriterImpl;

public class Client1 {
	
	static Socket clientSocket;
	 InputStream clientSockInput;
	OutputStream clientSockOutput;
	static ObjectOutputStream clientOOS;
	static ObjectInputStream clientOIS;
	PaxosMessageReader paxosMessageReader;
	PaxosMessageWriter paxosMessageWriter;

	public static void main(String args[]) throws ClassNotFoundException,IOException {
		Client1 cs = new Client1();
		//Starting the client1
		cs.startClient();
		
	}

	public void startClient() throws IOException, ClassNotFoundException {

		paxosMessageWriter = new PaxosMessageWriterImpl();
		paxosMessageReader = new PaxosMessageReaderImpl();
		
		/**
		 * Try establishing a connection to randomly picked server
		 */
		ClientUtilities clientUtilitiesObject = new ClientUtilities();
		//Host pickedServer = clientUtilitiesObject.pickRandomServerToConnect(Constants.totalNumberofServers);
		Host pickedServer = null;
		try {
			//clientSocket = new Socket(pickedServer.getHostName(), Integer.parseInt(pickedServer.getPortNumber()));
			while(clientSocket == null){
				try{
			
					pickedServer = clientUtilitiesObject.pickRandomServerToConnect(Constants.totalNumberofServers);
					clientSocket = new Socket(pickedServer.getHostName(), Integer.parseInt(pickedServer.getPortNumber()));
				}catch(SocketException se){
					System.out.println(String.format("Server : %s is down with port number : %s, Trying to connect to other server", pickedServer.getHostName(), pickedServer.getPortNumber()));
				}
			}
			System.out.println("Connection established to Server: " + "["+ pickedServer+"]" +"  "+pickedServer.getHostName()+ " through port : " +pickedServer.getPortNumber() );
		} catch (UnknownHostException e) {
			System.out.println(String.format("Server : %s is down with port number : %s, Trying to connect to other server", pickedServer.getHostName(), pickedServer.getPortNumber()));
			e.printStackTrace();
		} catch (Exception e){
			System.out.println("Could not get i/o connection");
			e.printStackTrace();
		}

		/*
		 Initialize input stream and output stream for initialized socket
		*/
		try {
			System.out.println("Initializing Output Stream..");
			clientSockOutput = clientSocket.getOutputStream();
			clientOOS = new ObjectOutputStream(clientSockOutput);
			
			clientSockInput = clientSocket.getInputStream();
			System.out.println( this.getClass().getCanonicalName() + " : Got input stream");
			//clientOIS = new ObjectInputStream(clientSockInput);
			
			System.out.println("Successfully initialized Object input and output Streams..");

			
			System.out.println( this.getClass().getName() +"Preparing Request_lock message to send to Server..");
			Message m = new Message();
			m.setMessageType(MessageType.REQUEST_LOCK);
			int lockToRequest=clientUtilitiesObject.pickRandomLockToRequestFromClient(Constants.totalNumberofLocks);
			System.out.println(ClientUtilities.getMessageSeparator());
			System.out.println("LOCKNUMBER REQUESTED:"+lockToRequest);
			m.setLockValue(lockToRequest);
			m.setClientId(this.getClass().getSimpleName());
			//requestLockMessageObj.setLock_Number(lockToRequest); 
			// TODO : Decide how clients should decide on requesting the desired lock ??
			
			// Send the request lock message to Connected Server (Proposers)
			System.out.println(ClientUtilities.getMessageSeparator());
			System.out.println(this.getClass().getName() + " Trying to write object to Stream");
			
			clientOOS.writeObject(m);
			clientOOS.flush();
			Thread.sleep(10000);
			
			// ONce the message is sent to proposers, Wait for the response from Server. 
			// Was the lock granted or not ??
			ResponseLockMessage  response = (ResponseLockMessage) paxosMessageReader.readMessage(clientSocket);
			if(response.isLockGranted()){
				System.out.println(ClientUtilities.getMessageSeparator());
				System.out.println("LOCK "+response.getLockValue()+" WAS GRANTED SUCCESSFULLY !!! "+this.getClass().getName());
				System.out.println(ClientUtilities.getMessageSeparator());
			} else {
				System.out.println(ClientUtilities.getMessageSeparator());
				System.out.println("FAILED TO GET LOCK " +response.getLockValue()+"!!");
				System.out.println(ClientUtilities.getMessageSeparator());
			}
			
			
		} catch (Exception e) {
			System.out.println("client input error");
			e.printStackTrace();
		}
	}
}